#include "MaterialManager.h"

#include "Material.h"
#include "StringVector.h"
#include "LogManager.h"
#include "Archive.h"
#include "StringConverter.h"
#include "BlendMode.h"
#include "Technique.h"
#include "Pass.h"
#include "TextureUnitState.h"
#include "Exception.h"
#include "LodStrategyManager.h"

namespace PVM
{
	template<> MaterialManager* Singleton<MaterialManager>::ms_Singleton = 0;
	MaterialManager* MaterialManager::getSingletonPtr(void)
	{
		return ms_Singleton;
	}
	
	MaterialManager& MaterialManager::getSingleton(void)
	{
		assert(ms_Singleton); return (*ms_Singleton);
	}
	String MaterialManager::DEFAULT_SCHEME_NAME = "Default";
	
	MaterialManager::MaterialManager() 
	{
		mDefaultMinFilter = FO_LINEAR;
		mDefaultMagFilter = FO_LINEAR;
		mDefaultMipFilter = FO_POINT;
		
		mSerializer = new MaterialSerializer();
		
		mLoadOrder = 100.0f;
		
		mResourceType = "Material";
		ResourceGroupManager::getSingleton()._registerResourceManager(mResourceType, this);
		
		mActiveSchemeIndex = 0;
		mActiveSchemeName = DEFAULT_SCHEME_NAME;
		mSchemes[mActiveSchemeName] = 0;
	}
	
	MaterialManager::~MaterialManager()
	{
		mDefaultSettings.setNull();
		ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType);
		ResourceGroupManager::getSingleton()._unregisterScriptLoader(this);
		
		delete mSerializer;
	}
	
	Resource* MaterialManager::createImpl(const String& name, ResourceHandle handle,
						const String& group, bool isManual, ManualResourceLoader* loader,
						const NameValuePairList* params)
	{
		return new Material(this, name, handle, group, isManual, loader);
	}
	
	void MaterialManager::initialise(void)
	{
		mDefaultSettings = create("DefaultSettings", ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
		mDefaultSettings->createTechnique()->createPass();
		
		mDefaultSettings->setLodStrategy(LodStrategyManager::getSingleton().getDefaultStrategy());
		create("BaseWhite", ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
		MaterialPtr baseWhiteNoLighting = create("BaseWhiteNoLighting",
												 ResourceGroupManager::INTERNAL_RESOURCE_GROUP_NAME);
		baseWhiteNoLighting->setLightingEnabled(false);
	}
	
	void MaterialManager::parseScript(DataStreamPtr& stream, const String& groupName)
	{
		mSerializer->parseScript(stream, groupName);
	}
	
	void MaterialManager::setDefaultTextureFiltering(TextureFilterOptions fo)
	{
		switch (fo)
		{
			case TFO_NONE:
				setDefaultTextureFiltering(FO_POINT, FO_POINT, FO_POINT);
				break;
			case TFO_BILINEAR:
				setDefaultTextureFiltering(FO_LINEAR, FO_LINEAR, FO_POINT);
				break;
			case TFO_TRILINEAR:
				setDefaultTextureFiltering(FO_LINEAR, FO_LINEAR, FO_LINEAR);
				break;
			case TFO_ANISOTROPIC:
				setDefaultTextureFiltering(FO_ANISOTROPIC, FO_ANISOTROPIC, FO_LINEAR);
				break;
		}
	}
	
	void MaterialManager::setDefaultAnisotropy(unsigned int maxAniso)
	{
		mDefaultMaxAniso = maxAniso;
	}
	
	unsigned int MaterialManager::getDefaultAnisotropy() const
	{
		return mDefaultMaxAniso;
	}
	
	void MaterialManager::setDefaultTextureFiltering(FilterType ftype, FilterOptions opts)
	{
		switch (ftype)
		{
			case FT_MIN:
				mDefaultMinFilter = opts;
				break;
			case FT_MAG:
				mDefaultMagFilter = opts;
				break;
			case FT_MIP:
				mDefaultMipFilter = opts;
				break;
		}
	}
	
	void MaterialManager::setDefaultTextureFiltering(FilterOptions minFilter,
			FilterOptions magFilter, FilterOptions mipFilter)
	{
		mDefaultMinFilter = minFilter;
		mDefaultMagFilter = magFilter;
		mDefaultMipFilter = mipFilter;
	}
	
	FilterOptions MaterialManager::getDefaultTextureFiltering(FilterType ftype) const
	{
		switch (ftype)
		{
			case FT_MIN:
				return mDefaultMinFilter;
			case FT_MAG:
				return mDefaultMagFilter;
			case FT_MIP:
				return mDefaultMipFilter;
		}
		
		return mDefaultMinFilter;
	}
	
	unsigned short MaterialManager::_getSchemeIndex(const std::string& schemeName)
	{
		unsigned short ret = 0;
		SchemeMap::iterator i = mSchemes.find(schemeName);
		if (i != mSchemes.end())
		{
			ret = i->second;
		}
		else
		{
			ret = static_cast<unsigned short>(mSchemes.size());
			mSchemes[schemeName] = ret;
		}
		return ret;
	}
	
	const std::string& MaterialManager::_getSchemeName(unsigned short index)
	{
		for (SchemeMap::iterator i = mSchemes.begin(); i != mSchemes.end(); ++i)
		{
			if (i->second == index)
				return i->first;
		}
		return DEFAULT_SCHEME_NAME;
	}
	
	unsigned short MaterialManager::_getActiveSchemeIndex(void) const
	{
		return mActiveSchemeIndex;
	}
	
	const std::string& MaterialManager::getActiveScheme(void) const
	{
		return mActiveSchemeName;
	}
	
	void MaterialManager::setActiveScheme(const std::string& schemeName)
	{
		if (mActiveSchemeName != schemeName)
		{
			mActiveSchemeIndex = _getSchemeIndex(schemeName);
			mActiveSchemeName = schemeName;
		}
	}
	
	void MaterialManager::addListener(Listener* l, const std::string& schemeName)
	{
		mListenerMap[schemeName].push_back(l);
	}
	
	void MaterialManager::removeListener(Listener* l, const std::string& schemeName)
	{
		mListenerMap[schemeName].remove(i);
	}
	
	Technique* MaterialManager::_arbitrateMissingTechniqueForActiveScheme(
		Material* mat, unsigned short lodIndex, const Renderable* rend)
	{
		ListenerMap::iterator it = mListenerMap.find(mActiveSchemeName);
		if (it != mListenerMap.end())
		{
			ListenerList& listenerList = it->second;
			for (ListenerList::iterator i = listenerList.begin(); i != listenerList.end(); ++i)
			{
				Technique* t = (*i)->handleSchemeNotFound(mActiveSchemeIndex,
								mActiveSchemeName, mat, lodIndex, rend);
				if (t)
					return t;
			}
		}
		
		it = mListenerMap.find(StringUtil::BLANK);
		if (it != mListenerMap.end())
		{
			ListenerList& listenerList = it->second;
			for (ListenerList::iterator i = listenerList.begin(); i != listenerList.end(); ++i)
			{
				Technique* t = (*i)->handleSchemeNotFound(mActiveSchemeIndex,
								mActiveSchemeName, mat, lodIndex, rend);
				if (t)
					return t;
			}
		}
		
		return 0;
	}
	
	
}
